
#include <ttg/all.h>
#include "PipelineTests.h"

using namespace ttg;
using namespace ttg::structures;
using namespace ttg::pipeline;

//Stores auxiliary information (to avoid usage of awful Triple structure):
//minmax + maximizing columns + true
// or
//maxmin + minimazing raws + false;
struct auxiliary
{
	int value;
	std::vector<size_t> indices;
	bool is_minmax;

	inline auxiliary()
	{ /*nothing*/ }

	inline auxiliary(int value, std::vector<size_t> indices, bool is_minmax)
	{
		this->value = value;
		this->indices = indices;
		this->is_minmax = is_minmax;
	}
};

//Finds minimum of row maximums.
//Sends minimum + indices of reaching columns + true.
class MinMaxer :public Node<matrix<int> *, auxiliary>
{
	virtual void process(matrix<int> *pdata)
	{
		matrix<int> &data = *pdata;
		std::vector<size_t> indices;
		int minmax = data(0, 0);
		//Processing each column.
		for (size_t j = 0; j < data.getWidth(); j++)
		{
			//Finding maximum of y-th column.
			int maximum = data.findColumnMaximum(j);
			//Is it a row with new minmax?
			if (minmax > maximum || j == 0)
			{
				indices.clear();
				indices.push_back(j);
				minmax = maximum;
			} else
			//Is it a column with current minmax?
			if (minmax == maximum)
			{ indices.push_back(j); }
		}
		//Sending result.
		sendNext(auxiliary(minmax, indices, true));
	}
};

//Finds maximum of column minimums.
//Sends maximum + indices of reaching rows + false.
class MaxMiner :public Node<matrix<int> *, auxiliary>
{
	virtual void process(matrix<int> *pdata)
	{
		matrix<int> &data = *pdata;
		std::vector<size_t> indices;
		int maxmin = data(0, 0);
		//Processing each row.
		for (size_t i = 0; i < data.getHeight(); i++)
		{
			int minimum = data.findRowMinimum(i);
			//Is it a column with new maxmin?
			if (maxmin < minimum || i == 0)
			{
				indices.clear();
				indices.push_back(i);
				maxmin = minimum;
			} else
			//Is it a row with current minmax?
			if (maxmin == minimum)
			{ indices.push_back(i); }
		}
		//Sending result.
		sendNext(auxiliary(maxmin, indices, false));
	}
};

//Finds potential saddle points.
class SaddlePointFinder :public Node<auxiliary, point<size_t>>
{
	private:
		auxiliary maxmin;
		auxiliary minmax;
	public:
		virtual void process(auxiliary data)
		{
			//Is it data from MinMaxer?
			if (data.is_minmax == true)
			{ minmax = data; }
			//It's data from MaxMiner!
			else
			{ maxmin = data; }
		}

		virtual void onEnd()
		{
			if (minmax.value == maxmin.value)
				//Generating saddle points as intersections of maximized rows and minimized columns.
				for (size_t j = 0; j < minmax.indices.size(); j++)
					for (size_t i = 0; i < maxmin.indices.size(); i++)
						sendNext(point<size_t>(maxmin.indices[i], minmax.indices[j]));
		}
};

int main()
{
	//Starting services.
	services::ServiceBase::getRef()->start();
	//Creating pipeline.
	Pipeline<matrix<int> *, point<size_t>> pl(wrap(new MaxMiner()) * wrap(new MinMaxer()) +
											  wrap(new SaddlePointFinder()));
	//Starting logging.
	//PipelineLogger("SaddlePoints.log");		//fix me: currently, this feature does not work.
	//Adding utilities.
	ttg::utils::UtilManager::getRef()->addUtility("UsageVisualizer");
	//Test #1.
	printf("Test #1 ... ");
	if (!pipelineTest1(pl))
		printf("failed\n");
	else
		printf("passed\n");
	//Test #2.
	printf("Test #2 ... ");
	if (!pipelineTest2(pl))
		printf("failed\n");
	else
		printf("passed\n");
	//Test #3.
	printf("Test #3 ... ");
	if (!pipelineTest3(pl))
		printf("failed\n");
	else
		printf("passed\n");
	//Test #4.
	printf("Test #4 ... ");
	if (!pipelineTest4(pl))
		printf("failed\n");
	else
		printf("passed\n");
	exit(0);
}
